<template>
	<view class="e-popup">
		<e-overlay :show="props.show" @click="overlayClick" v-if="props.overlay" :duration="overlayDuration"
			:customStyle="props.overlayStyle" :opacity="props.overlayOpacity"></e-overlay>
		<e-transition :show="props.show" :customStyle="transitionStyle" :mode="position" :duration="props.duration"
			@afterEnter="afterEnter" @click="clickHandler">
			<view class="e-popup__content" :style="[contentStyle]" @tap.stop="noop">
				<e-status-bar v-if="props.safeAreaInsetTop"></e-status-bar>
				<slot></slot>
				<view v-if="props.closeable" @tap.stop="close" class="e-popup__content__close"
					:class="['e-popup__content__close--' + props.closeIconPos]" hover-class="e-popup__content__close--hover"
					hover-stay-time="150">
					<e-icon name="close" color="#909399" size="18" bold></e-icon>
				</view>
				<e-safe-bottom v-if="props.safeAreaInsetBottom"></e-safe-bottom>
			</view>
		</e-transition>
	</view>
</template>
<script lang="ts">
	import {defineComponent} from 'vue'
	import mpMixin from '../../libs/mixin/mpMixin'
	/**
	 * popup 弹窗
	 * @description 弹出层容器，用于展示弹窗、信息提示等内容，支持上、下、左、右和中部弹出。组件只提供容器，内部内容由用户自定义
	 * @tutorial https://www.uviewui.com/components/popup.html
	 * @property {Boolean}			show				是否展示弹窗 (默认 false )
	 * @property {Boolean}			overlay				是否显示遮罩 （默认 true ）
	 * @property {String}			mode				弹出方向（默认 'bottom' ）
	 * @value top
	 * @value bottom
	 * @value right
	 * @value left
	 * @value center
	 * @property {String | Number}	duration			动画时长，单位ms （默认 300 ）
	 * @property {String | Number}	overlayDuration		遮罩层动画时长，单位ms （默认 350 ）
	 * @property {Boolean}			closeable			是否显示关闭图标（默认 false ）
	 * @property {Object | String}	overlayStyle		自定义遮罩的样式
	 * @property {String | Number}	overlayOpacity		遮罩透明度，0-1之间（默认 0.5）
	 * @property {Boolean}			closeOnClickOverlay	点击遮罩是否关闭弹窗 （默认  true ）
	 * @property {String | Number}	zIndex				层级 （默认 10075 ）
	 * @property {Boolean}			safeAreaInsetBottom	是否为iPhoneX留出底部安全距离 （默认 true ）
	 * @property {Boolean}			safeAreaInsetTop	是否留出顶部安全距离（状态栏高度） （默认 false ）
	 * @property {String}			closeIconPos		自定义关闭图标位置（默认 'top-right' ）
	 * @property {String | Number}	round				圆角值（默认 0）
	 * @property {Boolean}			zoom				当mode=center时 是否开启缩放（默认 true ）
	 * @property {String}			bgColor				弹窗背景色，设置为transparent可去除白色背景
	 * @property {Object}			customStyle			组件的样式，对象形式
	 * @event {Function} open 弹出层打开
	 * @event {Function} close 弹出层收起
	 * @example <e-popup :show="show"><text>出淤泥而不染，濯清涟而不妖</text></e-popup>
	 */
	
	export default defineComponent({
		name: 'e-popup',
		mixins: [mpMixin]
	})
</script>
<script lang="ts" setup>
	import {
		ref,
		watch,
		computed,
		toRefs,
		getCurrentInstance
	} from "vue";
	import type {
		Slot
	} from "vue";
	import {
		useDefaultProps
	} from '../../libs/hooks'
	import {
		popupProps,
		popupEmits
	} from "./props";
	import {
		deepMerge,
		sleep,
		// sys
	} from "../../libs/utils";
	import {
		addUnit,
		addStyle,
		noop
	} from "../../libs/utils/_utils";
	import type {
		StyleProperties
	} from "../../libs/tokens";
	import componentsProps, {
		type DefaultProps
	} from "../../libs/config/componentsProps";

	const {
		popup: popupDefaultProps
	} = toRefs(componentsProps);

	const _props = defineProps(popupProps)

	const props = useDefaultProps < DefaultProps['popup'] > (_props, popupDefaultProps);
	const emit = defineEmits(popupEmits);

	const ctx = getCurrentInstance();

	const overlayDuration = ref(Number(props.value.duration) + 50);

	watch(
		() => props.value.show,
		(newValue) => {
			if (newValue === true) {
				// #ifdef MP-WEIXIN
				ctx?.slots?.default && retryComputedComponentRect(ctx.slots.default);
				// #endif
			}
		}
	);

	const transitionStyle = computed(() => {
		const style: StyleProperties = {
			zIndex: props.value.zIndex,
			position: "fixed",
			// display: "flex",
		};
		style[props.value.mode] = 0;

		switch (props.value.mode) {
			case "left":
			case "right":
				return deepMerge(style, {
					bottom: 0,
					top: 0,
				});
			case "top":
			case "bottom":
				return deepMerge(style, {
					left: 0,
					right: 0,
				});
			case "center":
				return deepMerge(style, {
					alignItems: "center",
					"justify-content": "center",
					top: 0,
					left: 0,
					right: 0,
					bottom: 0,
				});
			default:
				return style;
		}
	});

	const contentStyle = computed(() => {
		const style: StyleProperties = {};
		// 通过设备信息的safeAreaInsets值来判断是否需要预留顶部状态栏和底部安全局的位置
		// 不使用css方案，是因为nvue不支持css的iPhoneX安全区查询属性
		// const {
		// 	safeAreaInsets
		// } = sys();
		if (props.value.mode !== "center") {
			style.flex = 1;
		}
		// 背景色，一般用于设置为transparent，去除默认的白色背景
		if (props.value.bgColor) {
			style.backgroundColor = props.value.bgColor;
		}
		if (props.value.round) {
			const value = addUnit(props.value.round);
			switch(props.value.mode){
				case "top":
				style.borderBottomLeftRadius = value;
				style.borderBottomRightRadius = value;
				break
				case "bottom":
				style.borderTopLeftRadius = value;
				style.borderTopRightRadius = value;
				break
				case "left":
				style.borderBottomRightRadius = value;
				style.borderTopRightRadius = value;
				break
				case "right":
				style.borderTopLeftRadius = value;
				style.borderBottomLeftRadius = value;
				break
				default:
				style.borderRadius = value;
			}
		}
		return deepMerge(style, addStyle(props.value.customStyle as Record<string,any>) as Record<string,any>);
	});
	const positionTypes: Record < string, string > = {
		left: "slide-left",
		right: "slide-right",
		bottom: "slide-up",
		top: "slide-down",
	};
	const position = computed(() => {
		if (props.value.mode === "center") {
			return props.value.zoom ? "fade-zoom" : "fade";
		} else {
			return positionTypes[props.value.mode];
		}
	});

	// 点击遮罩
	const overlayClick = () => {
		if (props.value.closeOnClickOverlay) {
			emit("close");
		}
	};
	const close = () => {
		emit("close");
	};
	const afterEnter = () => {
		emit("open");
	};
	const clickHandler = () => {
		// 由于中部弹出时，其e-transition占据了整个页面相当于遮罩，此时需要发出遮罩点击事件，是否无法通过点击遮罩关闭弹窗
		if (props.value.mode === "center") {
			overlayClick();
		}
	};
	// #ifdef MP-WEIXIN
	const retryComputedComponentRect = (
		children?: any
	) => {
		if (!children || typeof children === "string") return;
		// 组件内部需要计算节点的组件
		const names = [
			"e-calendar-month",
			"e-album",
			"e-collapse-item",
			"e-dropdown",
			"e-index-item",
			"e-index-list",
			"e-line-progress",
			"e-list-item",
			"e-rate",
			"e-read-more",
			"e-row",
			"e-row-notice",
			"e-scroll-list",
			"e-skeleton",
			"e-slider",
			"e-steps-item",
			"e-sticky",
			"e-subsection",
			"e-swipe-action-item",
			"e-tabbar",
			"e-tabs",
			"e-tooltip",
		];
		// 历遍所有的子组件节点
		for (let i = 0; i < children.length; i++) {
			const child = children[i];
			// 拿到子组件的子组件
			const grandChild = child.$children;
			// 判断如果在需要重新初始化的组件数组中名中，并且存在init方法的话，则执行
			if (
				names.includes(child.$options.name) &&
				typeof child?.init === "function"
			) {
				// 需要进行一定的延时，因为初始化页面需要时间
				sleep(50).then(() => {
					child.init();
				});
			}
			// 如果子组件还有孙组件，进行递归历遍
			if (grandChild.length) {
				retryComputedComponentRect(grandChild);
			}
		}
	};
	// #endif
</script>

<style lang="scss" scoped>
	@import '../../libs/theme/popup.scss';
</style>
